package org.jdbcparser.text.iterator;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.util.List;

import org.dbunit.VerifyTableDefinition;
import org.jdbcparser.database.RowSet;

import com.google.common.collect.AbstractIterator;
import com.google.common.io.CharSink;
import com.google.common.io.FileWriteMode;
import com.google.common.io.Files;

public abstract class BaseIterator extends AbstractIterator<List<RowSet>> {

	private static final int MAPPED_FILE_MEMORY_SIZE = 0x8FFFFFF;

	protected enum TextBlockMode {
		DELIMITER,
		SIZE
	}
	
	protected URL fileURL;
	protected TextBlockMode mode;
	protected Charset encoding;
	protected BufferedReader textReader;
	protected File sourceFile;
	protected boolean deleteSourceFile;
	private CharBuffer sourceFileReadBuffer;
	private int blockSize;
	private String blockDelimiter;
	
	protected void init() throws IOException, URISyntaxException {
		
		if(fileURL.getProtocol().equals("file")) {
			sourceFile = new File(fileURL.toURI());
		} else {
			sourceFile = copyToTmpFile();
			deleteSourceFile = true;
		}
		
		sourceFileReady(sourceFile);
		
	}
	
	protected void dispose() {
		if(sourceFile != null && deleteSourceFile) {
			sourceFile.delete();
		}
	}

	private File copyToTmpFile() throws IOException {
		
		InputStream urlStream = (InputStream) this.fileURL.getContent();
		BufferedReader urlReader = new BufferedReader(new InputStreamReader(urlStream, this.encoding));
		CharSink tmpFileSink = null;
		
		File tmpFile = File.createTempFile("jdbcparser", "tmp");
		tmpFile.deleteOnExit();
		tmpFileSink = Files.asCharSink(tmpFile, encoding, (FileWriteMode[]) null);
		tmpFileSink.writeFrom(urlReader); // Download file to tmp file

		urlReader.close();
		
		return tmpFile;
	}

	public void setFileURL(URL fileURL) {
		this.fileURL = fileURL;
	}

	// FIXME PROVIDE SEPARATOR
	protected void setLineSeparator() {
		this.mode = TextBlockMode.DELIMITER;
	}
	
	public void setEncoding(String encoding) {
		this.encoding = Charset.forName(encoding);
	}
	
	protected void sourceFileReady(File sourceFile) throws IOException {
		
		try {
			MappedByteBuffer inBuffer = new RandomAccessFile(sourceFile, "r").getChannel()
					.map(FileChannel.MapMode.READ_ONLY, 0, MAPPED_FILE_MEMORY_SIZE);

			sourceFileReadBuffer = encoding.decode(inBuffer).asReadOnlyBuffer();

		} catch (Exception e) {
			throw new IOException("Unable to read from file: '" + sourceFile.getAbsolutePath() + "'", e);
		}
	}
	
	protected List<RowSet> computeNext() {
		
		List<RowSet> next = null;
		String textBlock = nextTextBlock();
		
		while(textBlock != null) {
			next = parseRowSets(textBlock);
			if(next != null && !next.isEmpty()) {
				return next;
			}
			textBlock = nextTextBlock();
		}
        
        return endOfData();
    }
	
	protected String nextTextBlock() {

		switch(mode) {
		
		case DELIMITER:
			return nextDelimitedTextBlock();
		case SIZE:
			return nextFixedSizeTextBlock();
		}
		
		return null;
	}
	
	private String nextDelimitedTextBlock() {
		// FIXME USE START DELIMITER AND/OR END DELIMITER
	}
	
	private String nextFixedSizeTextBlock() {
		
		if(this.sourceFileReadBuffer.remaining() < blockSize) {
			return null;
		}
		
		char[] textBlockChars = new char[blockSize];
		this.sourceFileReadBuffer.get(textBlockChars, 0, blockSize);
		String textBlock = new String(textBlockChars);
		
		return textBlock;
	}
	
	protected List<RowSet> parseRowSets(String textBlock) {
		
	}

}
